/**  
  @file
 example_customer_at.c

  @brief
 the example code for the customer at commands.

*/
/*================================================================
  Copyright (c) 2020 Quectel Wireless Solution, Co., Ltd.  All Rights Reserved.
  Quectel Wireless Solution Proprietary and Confidential.
=================================================================*/
/*=================================================================

                        EDIT HISTORY FOR MODULE

This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.

WHEN              WHO         WHAT, WHERE, WHY
------------     -------     ------------------------------------------------
01/22/2025      Heath.Huang  Add MIDEA AT qcustcmd
=================================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/* include Quectel library header file */
#include "ql_oe.h"

#define  QL_CMD_ATCOP_NA 0X01
#define  QL_CMD_ATCOP_EQ 0X02
#define  QL_CMD_ATCOP_QU 0X04
#define  QL_CMD_ATCOP_AR 0X08

#define QL_IS_READ_COMMAND(opcode)     (opcode == (QL_CMD_ATCOP_NA | QL_CMD_ATCOP_QU))
#define QL_IS_WRITE_COMMAND(opcode)    (opcode == (QL_CMD_ATCOP_NA | QL_CMD_ATCOP_EQ | QL_CMD_ATCOP_AR))
#define QL_IS_EXEC_COMMAND(opcode)     (opcode == (QL_CMD_ATCOP_NA))
#define QL_IS_TEST_COMMAND(opcode)     (opcode == (QL_CMD_ATCOP_NA | QL_CMD_ATCOP_EQ | QL_CMD_ATCOP_QU))

#define	SYSLOG_DEBUG
#define SUCCESS 0
#define FAILURE -1
#define LICENSE_F_NAME "/data/midea/license_data"
#define VERSION_F_NAME "/data/midea/version"
#define APPRUNF_F_NAME "/data/midea/runapp_flag"
#define SECLOGF_F_NAME "/data/midea/sec_openlog"
#define SECNUM_F_NAME "/data/midea/secnum"

#define MIDEA_LICENSE_ONEC_LEN 	512//byte
#define MIDEA_LICENSE_PACK_LEN MIDEA_LICENSE_ONEC_LEN*2 //hex string
#define MAX_VALUE_LEN MIDEA_LICENSE_PACK_LEN+1
#define MIDEA_LICENSE_INDEX_MIN 0
#define MIDEA_LICENSE_INDEX_MAX 1
#define MIDEA_LICENSE_TOLTAL_LEN (MIDEA_LICENSE_INDEX_MAX+1)*MIDEA_LICENSE_PACK_LEN	//byte
#define MIDEA_LICENSE_BYTE2STR_LEN MIDEA_LICENSE_TOLTAL_LEN*2	//byte
#define MAX_FILE_SIZE MIDEA_LICENSE_TOLTAL_LEN*2  

#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))

typedef enum
{
    QL_SUCCESS = 0,
    QL_MEM_ADDR_NULL_ERR,
    QL_INVALID_PARAM_ERR,
} ql_errcode_e;
typedef struct
{
    int index;
    char data[MAX_VALUE_LEN];
} CmdArg ;


static char temp_license[MIDEA_LICENSE_TOLTAL_LEN+1] = {0};
static int temp_license_flag = 0;
static uint8_t index_set_flag = 0;
static CmdArg license_kv[4] = {0};


#ifdef SYSLOG_DEBUG

#include <stdint.h>
#include <sys/prctl.h>
#include <unistd.h>
#define SYSLOG_NAMES
#include <syslog.h>

#define __PNAME__   log_process_name
#define __PID__     log_process_id
#define PROCESS_NAME_LENGTH 48
#undef LOG_TAG
#define LOG_TAG             "atcmd"

int log_process_id;
char log_process_name[PROCESS_NAME_LENGTH];

static int quectel_log_initFlag = 1;
static int quectel_log_level = LOG_DEBUG;

static const char* getcodetext(int value, CODE *codetable) {
	CODE *i;

	if (value >= 0)
		for (i = codetable; i->c_val != -1; i++)
			if (i->c_val == value)
				return (i->c_name);
	return "<unknown>";
};


// 公共的日志格式宏
#define LOG_PRINT(level, LOG_TAG, fmt, ...) \
    printf("[%s][%s] [%s-%d][%s][%s][%d] " fmt "\n", \
           getcodetext(LOG_USER, facilitynames), getcodetext(level, prioritynames), \
           __PNAME__, __PID__, LOG_TAG, __func__, __LINE__, ##__VA_ARGS__)


// 具体的日志级别宏
#define LOGEM(LOG_TAG, fmt, ...)   do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_EMERG)) LOG_PRINT(LOG_EMERG, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGA(LOG_TAG, fmt, ...)    do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_ALERT)) LOG_PRINT(LOG_ALERT, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGC(LOG_TAG, fmt, ...)    do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_CRIT)) LOG_PRINT(LOG_CRIT, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGE(LOG_TAG, fmt, ...)    do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_ERR)) LOG_PRINT(LOG_ERR, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGW(LOG_TAG, fmt, ...)   do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_WARNING)) LOG_PRINT(LOG_WARNING, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGN(LOG_TAG, fmt, ...)   do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_NOTICE)) LOG_PRINT(LOG_NOTICE, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGI(LOG_TAG, fmt, ...)   do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_INFO)) LOG_PRINT(LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)
#define LOGD(LOG_TAG, fmt, ...)   do { if((1 == quectel_log_initFlag) && (quectel_log_level >= LOG_DEBUG)) LOG_PRINT(LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__); } while (0)		


#define log_process_info \
{   \
    log_process_id = getpid(); \
	prctl(PR_GET_NAME, (unsigned long)log_process_name, 0, 0, 0);\
	log_process_name[PROCESS_NAME_LENGTH - 1] = '\0';\
}

#endif 


typedef struct 
{
    char qlcmd_name[32];
	char qlcmd_argstr[512];
	char  qlcmd_opcode[8];
}ql_atcmd_type;

typedef int (* ql_atcmd_handler)(int,struct sockaddr_un *, char *, char *);

typedef struct
{
    char cmd_atname[32];
	ql_atcmd_handler cmd_athandler;
}ql_atcmd_handler_type;


int count_args(char *cmd_arg) {
    if (cmd_arg == NULL || (strlen(cmd_arg) == 0)) {
        return 0;  // 如果 cmd_arg 为 NULL，返回 0
    }

    int count = 0;
    char *ptr = cmd_arg;

    LOGD(LOG_TAG, " cmd_arg=%s\n",  cmd_arg);
    
    // 使用 strchr 查找逗号，并移动到下一个逗号的位置
    while (1) {
        ptr = strchr(ptr, ',');  // 查找逗号
        if (ptr != NULL) {
            LOGD(LOG_TAG, " Found comma, ptr=%s\n",  ptr);
            count++;
            ptr++;  // 跳过当前逗号，继续查找下一个逗号

            // 如果指针越过了字符串末尾，停止查找
            if (*ptr == '\0') {
                break;  // 跳出循环，避免访问空指针
            }

            LOGD(LOG_TAG, " Moving ptr to next character, ptr=%s\n",  ptr);
        } else {
            LOGD(LOG_TAG, " No more commas, ptr=%s\n",  ptr);
            break;  // 没有找到逗号，退出循环
        }
    }

    // 返回参数个数（逗号数 + 1）
    return count + 1;
}


// 提取参数并保存到结构体
ql_errcode_e parse_cmd_arg(const char *cmd_arg, CmdArg *cmd_args, int *arg_index) {
    char *token;
    char *saveptr;
    int count = 0, index = 0;
    char *cmd_copy;

    // 复制命令行参数，以便 strtok_r 修改
    cmd_copy = strdup(cmd_arg);
    if (cmd_copy == NULL) {
		LOGE(LOG_TAG, "strdup failed: %s\n", strerror(errno));
        return QL_MEM_ADDR_NULL_ERR;
    }
    LOGD(LOG_TAG, " cmd_arg=%s\n",  cmd_arg);
    // 通过 strtok_r 分割字符串
    token = strtok_r(cmd_copy, ",", &saveptr);
    while (token != NULL) {
        // 根据参数顺序保存
        if (count == 0) {
            index = atoi(token);  // 第一个参数作为整数赋值给 index
            if (index < 0 || index > 3) {  // 第一个参数必须在0到3之间
                LOGE(LOG_TAG, "Error: Invalid index value. Index must be between 0 and 3.\n");
				if (cmd_copy != NULL)
				{
                	free(cmd_copy);
					cmd_copy = NULL;
				}
                return QL_INVALID_PARAM_ERR;
            }
            cmd_args[index].index = index;  // 保存 index
        } else if (count == 1) {
            if (strlen(token) != (MAX_VALUE_LEN-1)) {  // 第二个参数不能超过512字节
                LOGE(LOG_TAG, "Error: Data exceeds %d bytes, ignoring data.\n", MAX_VALUE_LEN-1);
 				if (cmd_copy != NULL)
				{
                	free(cmd_copy);
					cmd_copy = NULL;
				}
                return QL_INVALID_PARAM_ERR;
            }
            strncpy(cmd_args[index].data, token, MAX_VALUE_LEN - 1);
            cmd_args[index].data[MAX_VALUE_LEN - 1] = '\0';  // 确保以 '\0' 结尾
			LOGD(LOG_TAG, " parse: cmd_args[cmd_cnt].index=%d, data=%s, strlen(data)=%d\n",  \
			cmd_args[index].index, cmd_args[index].data,strlen(cmd_args[index].data));
        }

        count++;
        token = strtok_r(NULL, ",", &saveptr);
    }

	if (cmd_copy != NULL)
	{
		free(cmd_copy);
		cmd_copy = NULL;
	}
	if (count == 2)
	{	
		*arg_index = index;
		return QL_SUCCESS;
	}
	else
		return QL_INVALID_PARAM_ERR; 
}


static int _save_data_to_file(const char *file_name, uint32_t offset, const char *buf, int len, int clean_flag)
{
    FILE *fp;

    // 检查文件打开模式

	if (1 == clean_flag)
	{
		fp = fopen(file_name, "wb+");  // 创建文件并打开
		if (fp == NULL) {
			LOGE(LOG_TAG, "Error creating file: %s\n", strerror(errno));
			return FAILURE;
		}	
	}
	else
	{
		// 打开文件，如果文件不存在则会被创建
		fp = fopen(file_name, "r+b");  // "r+b" 表示以读写模式打开文件，不存在时返回NULL
		if (fp == NULL) {
			if (errno == ENOENT) {  // 文件不存在
				fp = fopen(file_name, "wb+");  // 创建文件并打开
				if (fp == NULL) {
					LOGE(LOG_TAG, "Error creating file: %s\n", strerror(errno));
					return FAILURE;
				}
			} else {
				LOGE(LOG_TAG, "Error opening file: %s\n", strerror(errno));
				return FAILURE;
			}
		}
	}

    // 移动到指定偏移量
    if (fseek(fp, offset, SEEK_SET) != 0) {
		LOGE(LOG_TAG, "Error seeking in file: %s\n", strerror(errno));
        fclose(fp);
        return FAILURE;
    }

    // 写入数据
    size_t written = fwrite(buf, 1, len, fp);
    if (written != len) {
        LOGE(LOG_TAG, "Error: Failed to write data to file. Expected %d bytes, but wrote %zu bytes.\n", len, written);
        fclose(fp);
        return FAILURE;
    }

    // 同步文件数据到磁盘
    if (fsync(fileno(fp)) != 0) {
		LOGE(LOG_TAG, "Error syncing file: %s\n", strerror(errno));
        fclose(fp);
        return FAILURE;
    }

    fclose(fp);
    return SUCCESS;
}


// 读取文件内容并返回到 data 字符串
int _read_data_from_file(const char *file_name, char *data) {
    FILE *fp;                // 文件指针
    long file_size;          // 文件大小
    size_t read_len;         // 实际读取的字节数
    char *file_content = NULL;  // 用于存储文件内容
    int ret = SUCCESS;       // 返回值

    // 打开文件
    fp = fopen(file_name, "r");
    if (fp == NULL) {
		LOGE(LOG_TAG, "Error opening file: %s\n", strerror(errno));
        return FAILURE;
    }

    // 移动到文件末尾并获取文件大小
    if (fseek(fp, 0L, SEEK_END) != 0) {
		LOGE(LOG_TAG, "Error seeking in file: %s\n", strerror(errno));
        fclose(fp);
        return FAILURE;
    }

    // 获取文件大小
    file_size = ftell(fp);
    if (file_size == -1) {
		LOGE(LOG_TAG, "Error getting file size: %s\n", strerror(errno));
        fclose(fp);
        return FAILURE;
    }

    // 如果文件大小超出了最大限制，返回错误
    if (file_size > MAX_FILE_SIZE) {
        LOGE(LOG_TAG, "File is too large! Max allowed size is %d bytes\n", MAX_FILE_SIZE);
        fclose(fp);
        return FAILURE;
    }

    // 将文件指针重置到文件开头
    rewind(fp);

    // 为文件内容分配内存
    file_content = (char *)malloc(file_size + 1);  // +1 以便最后存放 '\0'
    if (file_content == NULL) {
		LOGE(LOG_TAG, "Error allocating memory for file content: %s\n", strerror(errno));
        fclose(fp);
        return FAILURE;
    }

    // 读取文件内容
    read_len = fread(file_content, 1, file_size, fp);
    if (read_len != file_size) {
		LOGE(LOG_TAG, "Error reading file content: %s\n", strerror(errno));
        free(file_content);
        fclose(fp);
        return FAILURE;
    }

    // 确保字符串以 '\0' 结尾
    file_content[file_size] = '\0';

    // 将文件内容拷贝到传入的 data 字符串
    strncpy(data, file_content, file_size);

    // 释放分配的内存和关闭文件
    free(file_content);
    fclose(fp);

    return ret;
}


// at+qcustcmd="MIDEAFAC?"
static int MIDEAFAC_get_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = -1;
	char response[512] = { 0 }, read_buf[32] = {0};

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (0 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	memset(read_buf, 0, sizeof(read_buf));
	if (_read_data_from_file(APPRUNF_F_NAME, read_buf) == SUCCESS) {
		LOGD(LOG_TAG, " file:%s, read_data = %s\n",  APPRUNF_F_NAME, read_buf);
	} 
	else
	{
		LOGE(LOG_TAG, "read file:%s, read_data = %s fail!\n",  APPRUNF_F_NAME, read_buf);
		goto err;
	}

	snprintf(response, sizeof(response)-1, "\"+%s:%s\"", cmd_name, read_buf);
	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;

}


//TODO RUN APP
// at+qcustcmd="MIDEAFAC",0/1
static int MIDEAFAC_set_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = -1;
	char response[512] = { 0 }, read_buf[32] = {0};
	char * flag = cmd_arg;

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (1 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	//TODO RUN APP
	if (strcmp(cmd_arg, "0") == 0)
	{
		//0:MAIN UART接收AT指令，不跑客户APP
		LOGD(LOG_TAG, " cmd_arg = %s, no run customer app\n",  cmd_arg);
	}
	else if (strcmp(cmd_arg, "1") == 0)    
	{
		// 1:MAIN UARTB不接收AT指令，跑客户APP
		LOGD(LOG_TAG, " cmd_arg = %s, run customer app\n",  cmd_arg);
	}
	else
	{
		LOGE(LOG_TAG, " cmd_arg = %s, error\n",  cmd_arg);
		goto err;
	}

	memset(read_buf, 0, sizeof(read_buf));
	if (_read_data_from_file(APPRUNF_F_NAME, read_buf) == SUCCESS) {
		LOGD(LOG_TAG, "read file:%s, read_data = %s\n",  APPRUNF_F_NAME, read_buf);
	} 

	if (strcmp(read_buf, cmd_arg) != 0)
	{
		ret = _save_data_to_file(APPRUNF_F_NAME, 0, flag, strlen(flag),1);
		LOGD(LOG_TAG, " file:%s, new flag save=%d\n",  APPRUNF_F_NAME, ret);
	}

	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;

}


int quec_midea_get_license(char* read_buffer, int read_size)
{
	memset(read_buffer, 0, read_size);
	if (_read_data_from_file(LICENSE_F_NAME, read_buffer) == SUCCESS) {
		LOGD(LOG_TAG, "read file:%s, read_buffer = %s\n",  LICENSE_F_NAME, read_buffer);
	} 	
}


// at+qcustcmd="MIDEALIC?" 
static int read_cnt = 1; 
static int MIDEALIC_get_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	char LICENSE_BUF[MIDEA_LICENSE_TOLTAL_LEN+1] = {0};
	char   			 res_buf[MIDEA_LICENSE_BYTE2STR_LEN+128] = {0};//send out hex string, need a buffer double size than byte_array
	int retval = 0;
	char tmp_buf[2000] = {0};
	
	
	LOGD(LOG_TAG, " read_cnt=%d, cmd_name=%s, cmd_arg=%s strlen(cmd_arg)=%d\n", read_cnt, cmd_name, cmd_arg, strlen(cmd_arg));
	if(0 < strlen(cmd_arg))
	{
		LOGE(LOG_TAG, " error cmd_arg = %s\n",  cmd_arg);
		goto err;
	}

	quec_midea_get_license(LICENSE_BUF, sizeof(LICENSE_BUF));

	// snprintf(res_buf, sizeof(res_buf)-1, "\"+%s:%s\"", cmd_name, LICENSE_BUF);
	memset(tmp_buf, 0, sizeof(tmp_buf));
	memset(res_buf, 0, sizeof(res_buf));
	// sprintf(&res_buf[strlen(res_buf)],"%s",LICENSE_BUF);
	// sprintf(&res_buf[strlen(res_buf)], "\"");

	if(read_cnt == 1)
	{
		memcpy((void *)tmp_buf, LICENSE_BUF, 1024);
	}
	else if(read_cnt == 2)
	{
		memcpy((void *)tmp_buf, LICENSE_BUF+1024, strlen(LICENSE_BUF)-1024);
	}
	LOGD(LOG_TAG, "strlen(tmp_buf)=%d \n tmp_buf=%s",strlen(tmp_buf), tmp_buf);
	sprintf(&res_buf[strlen(res_buf)], "^MIDEALIC: \"");
	sprintf(&res_buf[strlen(res_buf)], "index:%d \n",read_cnt);	
	sprintf(&res_buf[strlen(res_buf)],"%s",tmp_buf);
	sprintf(&res_buf[strlen(res_buf)], "\"");
	LOGD(LOG_TAG, "strlen(res_buf)=%d \n res_buf=%s",strlen(res_buf), res_buf);
	read_cnt++;
	if (read_cnt>2)
	{
		read_cnt = 1;
	}
	retval = ql_customer_at_send_response(sockfd, un, true, res_buf, strlen(res_buf));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;

}



// at+qcustcmd="MIDEALIC",0,XhHBxsBnKCYShCHjTcEM5eKfFFCyibzy43rS4zwPh15DAjNpjanDBbpnRHMr7cYb7x0RMmNnahQTpnsZRhP7yNNStchFW8szDZ29JKY64QMx8XbY44C09N7ycykTnGQdRZtDwx7P7zsZPSGiG3hzRzRWGWQKwGC7pYKzkfC2hNnwBGzd3Y7z3jaF72D3WNZ4fkSiB59HJkpFKsmrtwNNp10sBpf6Y6MzEN2H4sckWspcdYYXfY2ary7c0BNesKdQWJm4F1GFrBGrjkxtPpck87cxDQKyxQA4FrjCd0rSrRkcfS9XBmFap1xJcF3FXeNXQY2BCjKFCcYmiHkmTwMeSkDNy8AsPQcyK7DmYYnnSSC9W9SNADYYMXafmXr7SAsktYGJCkrFYksRzT3JC3JGzFb3rbMhNPjwX4cPBmzFZD5T3nm9BcBTpAca5k1Fr31kZ9D3fZ8rZG4pRH9AyYbX2hatRzPhQMCrTzfCdcB1tfN302RfjaYBwXb4ZRizyxNT
// at+qcustcmd="MIDEALIC",index,"licences" //licences大概2K，能一次写进就一次写入。保存并做备份
static int MIDEALIC_set_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	char response[512] = { 0 };
	int retval = 0, arg_cnt = 0, ret = -1;
	static int index = 0;
	struct stat st;
    char             info[MIDEA_LICENSE_PACK_LEN+1] = {0};
	// char   			 res_buf[4096+128] = {0};//send out hex string, need a buffer double size than byte_array

	ret = parse_cmd_arg(cmd_arg, license_kv, &index);
	LOGD(LOG_TAG, " index=%d, license_kv[index].index=%d, data=%s, strlen(data)=%d\n",  \
	index, license_kv[index].index, license_kv[index].data,strlen(license_kv[index].data));
	if (ret == QL_INVALID_PARAM_ERR || ret == QL_MEM_ADDR_NULL_ERR)
	{
		LOGE(LOG_TAG, " err ret %d\n",  ret);
		goto err;
	}
	
	memcpy((void *)info, license_kv[index].data, strlen(license_kv[index].data) + 1);
	if(license_kv[index].index == index_set_flag && strlen(license_kv[index].data) <= MIDEA_LICENSE_PACK_LEN)
	{
		if(index_set_flag == MIDEA_LICENSE_INDEX_MIN)
		{
			memset(temp_license, 0, sizeof(temp_license));//if index_set_flag == 0 ,clear license buff and start write new data
			temp_license_flag = 0;
		}    

		LOGD(LOG_TAG, " temp_license_flag=%d\n", temp_license_flag);  
		// LOGD(LOG_TAG, " info=%s, temp_license_flag=%d\n",  info, temp_license_flag);         
		//sprintf(&temp_license[strlen(temp_license)],"%s",info);
#if 0
		int byte_count = hex_string_to_byte_array(info, (unsigned char*)(temp_license + temp_license_flag), strlen(info));
#else
		int byte_count = strlen(info);
		memcpy(temp_license + temp_license_flag, (void *)info, strlen(info));
#endif

		if (strcmp(temp_license, "") == 0)
		{
			LOGE(LOG_TAG, " temp_license=%s,temp_license_flag=%d,licensn data err, don't write NV! \n", \
			 temp_license, temp_license_flag);
			snprintf(response, sizeof(response)-1, "\"+%s:%s\"", cmd_name,  "licensn data err, don't write NV!");
			goto err;
		}

		temp_license_flag = temp_license_flag + byte_count;
		LOGD(LOG_TAG, " byte_count=%d,temp_license_flag=%d\n temp_license=%s\n", temp_license_flag, byte_count, temp_license );
	}
	else
	{
		snprintf(response, sizeof(response)-1, "\"+%s:check index(Follow the previous one) and data lens(<=%d)\"", cmd_name, MIDEA_LICENSE_PACK_LEN);
		goto err;
	}

	if(index_set_flag == MIDEA_LICENSE_INDEX_MAX)//if index_set_flag == MIDEA_LICENSE_INDEX_MAX , write the whole string in NV, then reset index_set_flag
	{
		LOGD(LOG_TAG, " temp_license_flag=%d\n temp_license=%s\n", temp_license_flag, temp_license);

		if ( _save_data_to_file(LICENSE_F_NAME, 0, (char*)temp_license, temp_license_flag, 0) == 0)
		{
			// snprintf(response, sizeof(response)-1, "\"+%s:%s\"", cmd_name, "license write NV success");
			// sprintf(&response[strlen(response)], "\"+%s:now index is %d, byte count %d \" \n", cmd_name,  index_set_flag, temp_license_flag);
			snprintf(response, sizeof(response)-1, "\"+%s:now index %d,count %d %s\"", cmd_name, index_set_flag, temp_license_flag,\
			"license write NV success");
			index_set_flag = 0;
		}
		else
		{
			snprintf(response, sizeof(response)-1, "\"+%s:%s\"", cmd_name,  "license write NV fail");
			goto err;
		}


		if (stat(LICENSE_F_NAME, &st) == 0) {
			// 返回文件大小
			LOGD(LOG_TAG, " st.st_size=%d\n",  st.st_size);
		} else {
			LOGE(LOG_TAG, "stat failed: %s\n", strerror(errno));
		}

	}//if index_set_flag < MIDEA_LICENSE_INDEX_MAX , index_set_flag add 1
	else if(index_set_flag < MIDEA_LICENSE_INDEX_MAX)
	{
		snprintf(response, sizeof(response)-1, "\"+%s:now index is %d,next %d, byte count %d \"", cmd_name,  index_set_flag, index_set_flag+1, temp_license_flag);
		index_set_flag++;
	}
	else// invalid index_set_flag value, reset it
	{
		index_set_flag = 0;
		goto err;
	}
	
	// snprintf(response, sizeof(response)-1, "\"+%s\"", cmd_name);
	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	// memset((void *)&license_kv[cmd_cnt], 0, sizeof(CmdArg));
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;

}


// at+qcustcmd="CUSFOTA?"
static int CUSFOTA_get_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = -1;
	char response[512] = { 0 }, version[32] = {0};

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (0 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	memset(version, 0, sizeof(version));
	if (_read_data_from_file(VERSION_F_NAME, version) == SUCCESS) {
		LOGD(LOG_TAG, "read file:%s, version = %s\n",  VERSION_F_NAME, version);

	} 

	snprintf(response, sizeof(response)-1, "\"+%s:%s\"", cmd_name, version);
	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;
}


// at+qcustcmd="CUSFOTA",EG25GA
static int CUSFOTA_set_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = -1;
	char response[512] = { 0 }, read_buf[32] = {0};
	char * version = cmd_arg;

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (1 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	LOGD(LOG_TAG, " version=%s\n",  version);

	memset(read_buf, 0, sizeof(read_buf));
	if (_read_data_from_file(VERSION_F_NAME, read_buf) == SUCCESS) {
		LOGD(LOG_TAG, "read file:%s, read_data = %s\n",  VERSION_F_NAME, read_buf);

	} 

	if (strcmp(read_buf, cmd_arg) != 0)
	{
		ret = _save_data_to_file(VERSION_F_NAME, 0, version, strlen(version),1);
		LOGD(LOG_TAG, "save file:%s, new version save=%d\n",  VERSION_F_NAME, ret);
	}

	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;

}


// at+qcustcmd="MIDEALOG?"
static int MIDEALOG_get_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = -1;
	char response[512] = { 0 }, log_switch[32] = {0};

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (0 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	snprintf(response, sizeof(response)-1, "\"+%s:%d\"", cmd_name, quectel_log_initFlag);
	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;
}


// 功能1：写入imei+cusversion前6位，保存并备份，功能2：客户日志控制，传“0”关闭客户日志输出，掉电不保存
// at+qcustcmd="MIDEALOG",864430010001091EC25GA
static int MIDEALOG_set_handler(int sockfd, struct sockaddr_un *un, char *cmd_name, char *cmd_arg)
{
	int arg_cnt = 0, retval = 0, ret = 0;
	char response[512] = { 0 };
	char imei[32] = {0}, version[32] = {0}, ver_result[7] = {0}, read_passwd[32] = {0};

	arg_cnt = count_args(cmd_arg);
	LOGD(LOG_TAG, " cmd_name=%s, cmd_arg=%s ,arg_cnt=%d\n",  cmd_name, cmd_arg, arg_cnt);

	if (1 != arg_cnt)
	{
		LOGE(LOG_TAG, " err arg_cnt=%d\n",  arg_cnt);
		goto err;
	}

	LOGD(LOG_TAG, " strlen(cmd_arg)=%d, quectel_log_initFlag=%d\n",  strlen(cmd_arg),quectel_log_initFlag);

	ret = QL_DEV_GetImei(imei, sizeof(imei));
	LOGD(LOG_TAG, " ret = %d, IMEI = %s\n",  ret, imei);

	memset(version, 0, sizeof(version));
	if (_read_data_from_file(VERSION_F_NAME, version) == SUCCESS) {
		LOGD(LOG_TAG, "read file:%s, read_data = %s\n",  VERSION_F_NAME, version);
	}
	if (strlen(version) >= 6)
	{
		strncpy(ver_result, version, 6);
		ver_result[6] = '\0';			
	}
	snprintf(response, sizeof(response)-1, "%s%s", imei, ver_result);
	LOGD(LOG_TAG, " IMEI+VERSION = %s\n",  response);


	if (strcmp(cmd_arg, response) == 0)
	{
		quectel_log_initFlag = 1;
		LOGD(LOG_TAG, " password success, open log! quectel_log_initFlag=%d\n", quectel_log_initFlag);

		memset(read_passwd, 0, sizeof(read_passwd));
		if (_read_data_from_file(SECNUM_F_NAME, read_passwd) == SUCCESS) {
			LOGD(LOG_TAG, "read file:%s, read_passwd = %s\n",  SECNUM_F_NAME, read_passwd);
		} 

		if (strcmp(read_passwd, response) != 0)
		{
			ret = _save_data_to_file(SECNUM_F_NAME, 0, response, strlen(response), 1);
			LOGD(LOG_TAG, "save file:%s, new version save=%d\n",  SECNUM_F_NAME, ret);
		}

	}
	else if(strcmp(cmd_arg, "0") == 0)	
	{
		LOGD(LOG_TAG, "close log! \n");
		//关闭log
		quectel_log_initFlag = 0;
	}
	else
	{
		LOGE(LOG_TAG, " file:%s, cmd_arg=%s,strlen(cmd_arg)=%d, password=%s,strlen(password)=%d\n",  \
		SECNUM_F_NAME, cmd_arg,strlen(cmd_arg), response, strlen(response));
		LOGE(LOG_TAG, " password err, cannot open log!\n");
		goto err;
	}

	memset(response, 0, sizeof(response));
	retval = ql_customer_at_send_response(sockfd, un, true, response, strlen(response));
	return retval;

err:
	retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
	return retval;
}



static char cust_atcmd[][16] = 
{
	"MIDEAFAC?",
	"MIDEAFAC",
	"MIDEALIC?",
	"MIDEALIC",
	"CUSFOTA?",
	"CUSFOTA",
	"MIDEALOG?",
	"MIDEALOG"
};

ql_atcmd_handler_type cust_atcmd_handler[]=
{
	{"MIDEAFAC?", MIDEAFAC_get_handler},
	{"MIDEAFAC", MIDEAFAC_set_handler},
	{"MIDEALIC?", MIDEALIC_get_handler},
	{"MIDEALIC", MIDEALIC_set_handler},
	{"CUSFOTA?", CUSFOTA_get_handler},
	{"CUSFOTA", CUSFOTA_set_handler},
	{"MIDEALOG?", MIDEALOG_get_handler},
	{"MIDEALOG", MIDEALOG_set_handler}
};

static int quectel_check_custcmd_func(char * name)
{
	int i = 0;
	for(i = 0; i < (ARRAY_SIZE(cust_atcmd)); i++)
	{
		LOGD(LOG_TAG, "cust atcmd name %s\n", name);
		if(0 == strcasecmp(cust_atcmd[i], name))
		{
			LOGD(LOG_TAG, "found cust at command success\n");
			return 0;
		}
	}
	return -1;
}



static int quectel_get_atcmd_name(char * str, ql_atcmd_type * atcmd)
{
    char * tmp1 = NULL;
    char * tmp2 = NULL;

	tmp1 = strchr(str, '\"');
	if(tmp1 == NULL)
	{
	    return -1;
	}

	tmp2 = strchr(tmp1 + 1, '\"');
	if(tmp2 == NULL)
	{
	    return -1;
	}

	memcpy(atcmd->qlcmd_name, tmp1+1, tmp2-tmp1-1);

    LOGD(LOG_TAG, "cmd name: %s size: %d\n", atcmd->qlcmd_name, strlen(atcmd->qlcmd_name));
    return 0;
}

static int quectel_get_atcmd_paramter(char * str, ql_atcmd_type * atcmd)
{
    char * tmp = NULL;
	
	tmp = strchr(str, ',');
	if(tmp == NULL)
	{
	    return -1;
	}
	strcpy(atcmd->qlcmd_argstr, tmp+1);

    LOGD(LOG_TAG, "cmd strarg: %s size: %d\n", atcmd->qlcmd_argstr, strlen(atcmd->qlcmd_argstr));
    return 0;
}


static void atcommand_handler(int sockfd, struct sockaddr_un *un, char *ptr, int len)
{
	int retval;
	char response[512] = { 0 };
    ql_atcmd_type recv_atcmd;
	int i = 0;

    LOGD(LOG_TAG, "cmd str: %s\n", ptr);

    memset(&recv_atcmd, 0, sizeof(recv_atcmd));
    //get atcmd name 
    if(0 > quectel_get_atcmd_name(ptr, &recv_atcmd))
	{
        LOGE(LOG_TAG, "no found at command\n");
        retval = ql_customer_at_send_response(sockfd, un, false, response, strlen(response));
		return;
	}

	//get atcmd parameter
	quectel_get_atcmd_paramter(ptr, &recv_atcmd);

    if(0 > quectel_check_custcmd_func(recv_atcmd.qlcmd_name))
	{
	    LOGE(LOG_TAG, "not found customer at cmd\n");
		retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
		return;

	}
    else
    {
		for(i = 0; i < (ARRAY_SIZE(cust_atcmd_handler)); i++)
		{
	       if(0 == strcasecmp(cust_atcmd_handler[i].cmd_atname, recv_atcmd.qlcmd_name))
	       {
			   cust_atcmd_handler[i].cmd_athandler(sockfd, un, recv_atcmd.qlcmd_name, recv_atcmd.qlcmd_argstr);
		       return;
	       }
		}
       //not found customer at command handler,return false
       retval = ql_customer_at_send_response(sockfd, un, false, NULL, 0);
       return;
 
    }
}

static void listen_sock(int sockfd)
{
	int retval;
	fd_set rfds;
	struct timeval tv;
	struct sockaddr_un un;
	char buf[2048];

	while (1) 
	{
		FD_ZERO(&rfds);
		FD_SET(sockfd, &rfds);

		tv.tv_sec = 5;
		tv.tv_usec = 0;

		LOGD(LOG_TAG, "wait select.... \n");
		retval = select(sockfd + 1, &rfds, NULL, NULL, &tv);
		if (retval > 0) 
		{
			if(FD_ISSET(sockfd, &rfds)) 
			{
				memset(buf, 0, sizeof(buf));
				memset(&un, 0, sizeof(un));
				if (ql_customer_at_get_request(sockfd, &un, buf, sizeof(buf))) 
				{
					LOGE(LOG_TAG, "get at command request failed\n");
					continue;
				}
				atcommand_handler(sockfd, &un, buf, strlen(buf));
			}
		} 
		else if (retval < 0)
		{
			LOGE(LOG_TAG, "select error: %s\n", strerror(errno));
		}
	}
}

int main(int argc, char **argv)
{
	int sockfd = -1;

	log_process_info;
	sockfd = ql_customer_at_get_socket();
	if (sockfd < 0) 
	{
		LOGE(LOG_TAG, "Can not get socket fd\n");
		return -1;
	}

	listen_sock(sockfd);
	return 0;
}
